#include "xen/renderer/material.hpp"

namespace Xen {

Ref<Material> Material::Create(const Ref<Shader>& shader) {
    return std::make_shared<Material>(shader);
}

Material::Material(const Ref<Shader> &shader): shader_(shader) {
}

void Material::SetData(const std::string &name, const ShaderUniformData &prop) {
    datas_[name] = prop;
}

void Material::SetTexture(const std::string& name, const Ref<Texture2D>& texture) {
    textures_[name] = texture;
}

ShaderUniformData Material::GetData(const std::string& name) const {
    auto it = datas_.find(name);
    if (it == datas_.end()) {
        return ShaderUniformData();
    }
    return it->second;
}

Ref<Texture2D> Material::GetTexture(const std::string& name) const {
    auto it = textures_.find(name);
    if (it == textures_.end())
        return nullptr;
    return it->second;
}

void Material::Use() {
    for (auto& data : datas_) {
        uniformShaderData(data.first, data.second);
    }
    uint32_t slot_idx = 0;
    for (auto& texture : textures_) {
        texture.second->Bind(slot_idx);
        shader_->UniformInt(texture.first, slot_idx);
        slot_idx++;
    }
}

void Material::uniformShaderData(const std::string& name, const ShaderUniformData& data) {
    switch (data.type) {
        case ShaderDataType::None:
            break;
        case ShaderDataType::Float:
            shader_->UniformFloat(name, std::get<float>(data.data));
            break;
        case ShaderDataType::Float2:
            shader_->UniformFloat2(name, std::get<glm::vec2>(data.data));
            break;
        case ShaderDataType::Float3:
            shader_->UniformFloat3(name, std::get<glm::vec3>(data.data));
            break;
        case ShaderDataType::Float4:
            shader_->UniformFloat4(name, std::get<glm::vec4>(data.data));
            break;
        case ShaderDataType::Mat3:
            shader_->UniformMat3(name, std::get<glm::mat3>(data.data));
            break;
        case ShaderDataType::Mat4:
            shader_->UniformMat4(name, std::get<glm::mat4>(data.data));
            break;
        case ShaderDataType::Int:
            shader_->UniformInt(name, std::get<int>(data.data));
            break;
        case ShaderDataType::Int2:
            shader_->UniformInt2(name, std::get<glm::ivec2>(data.data));
            break;
        case ShaderDataType::Int3:
            shader_->UniformInt3(name, std::get<glm::ivec3>(data.data));
            break;
        case ShaderDataType::Int4:
            shader_->UniformInt4(name, std::get<glm::ivec4>(data.data));
            break;
        default:
            XEN_CORE_ASSERT(false, "Unknown shader data type");
    }
}

}
